import { Ceno } from "./core/ceno";
import { requestMethods } from "./core/constants";
import { CenoInterceptor } from "./core/interceptor";
import { CenoInstance } from "./types/ceno";
import { Input, Options } from "./types/options";
import { validateAndMerge } from "./utils/merge";
import { Mutable } from "./utils/types";

const InitInstance = (defaults?: Partial<Options>): CenoInstance => {
    const interceptor = new CenoInterceptor()

    if (defaults?.interceptors) {
        interceptor.use(defaults.interceptors)
        const { interceptors, ...cleanDefaults } = defaults
        defaults = cleanDefaults
    }

    const ceno: Partial<Mutable<CenoInstance>> = (input: Input, options?: Options) => Ceno.create(input, validateAndMerge(defaults, options), interceptor)

    for (const method of requestMethods) {
        ceno[method] = (input: Input, options?: Options) => Ceno.create(input, validateAndMerge(defaults, options, { method }), interceptor)
    }

    ceno.create = (newDefaults?: Partial<Options>) => InitInstance(validateAndMerge(newDefaults))
    ceno.extend = (newDefaults?: Options | ((parentOptions: Options) => Options)) => {
        if (typeof newDefaults === 'function') {
            newDefaults = newDefaults(defaults ?? {})
        }

        return InitInstance(validateAndMerge(defaults, newDefaults, { interceptors: interceptor.snapshot() }))
    }

    ceno.interceptors = interceptor

    return ceno as CenoInstance
}

const ceno = InitInstance()

export default ceno

export { HttpError } from "./errors/http-error"
export { TimeoutError } from "./errors/timeout-error"

export type { CenoInstance } from "./types/ceno"
export type { CenoPromise } from "./types/promise"
export type { CenoRequest } from "./types/request"
export type { CenoResponse } from "./types/response"

